package top.jle.wx.proxy.facade.impl;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import top.jle.jpa.basic.dao.PagedResult;
import top.jle.jpa.basic.exception.BasicCustomException;
import top.jle.jpa.basic.exception.BasicExceptionCode;
import top.jle.jpa.basic.util.ExceptionUtils;
import top.jle.wx.proxy.api.ResponseBack;
import top.jle.wx.proxy.domain.material.Article;
import top.jle.wx.proxy.exception.ExceptionCode;
import top.jle.wx.proxy.facade.BasicFacade;
import top.jle.wx.proxy.facade.MaterialFacade;
import top.jle.wx.proxy.feign.ResultCheckUtil;
import top.jle.wx.proxy.feign.material.GetMaterialAPIFeign;
import top.jle.wx.proxy.feign.material.MaterialAPIFeign;
import top.jle.wx.proxy.util.GlobalThreadLocalUtil;

import feign.Response;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
//import org.apache.http.Header;
//import org.apache.http.client.methods.CloseableHttpResponse;
//import org.apache.http.client.methods.HttpPost;
//import org.apache.http.entity.StringEntity;
//import org.apache.http.impl.client.CloseableHttpClient;
//import org.apache.http.impl.client.HttpClients;
//import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import top.jle.wx.proxy.constant.MaterialOption;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;


@Component("materialFacade")
public class MaterialFacadeImpl implements MaterialFacade {

    private static final Logger LOGGER = LoggerFactory.getLogger(MaterialFacadeImpl.class);


    @Autowired
    private MaterialAPIFeign materialAPIFeign;

    @Autowired
    private GetMaterialAPIFeign getMaterialAPIFeign;

    @Autowired
    private BasicFacade basicFacade;


    @Override
    public MaterialOption.TemporaryMaterialResponse addTemporary(String type, MultipartFile file) {
        MaterialOption.MaterialType materialType = EnumUtils.getEnum(MaterialOption.MaterialType.class, type);
        if (materialType == null || file == null) {
            ExceptionUtils.throwInvalidParamException();
        }
        JSONObject result = materialAPIFeign.addTemporary(type, file);
        if (!ResultCheckUtil.check(result)) {
            LOGGER.error(result.toString());
            throw new BasicCustomException(ExceptionCode.UPLOAD_TEMPORARY_FAIL);
        }
        MaterialOption.TemporaryMaterialResponse response = result.toJavaObject(MaterialOption.TemporaryMaterialResponse.class);
        response.setExpire_at(response.getCreated_at() + MaterialOption.TEMPORARY_MATERIAL_EXPIRE_SECONDS);
        return response;
    }

    @Override
    public ResponseEntity<byte[]> getTemporary(String mediaId) {
        if ( StringUtils.isBlank(mediaId)) {
            ExceptionUtils.throwInvalidParamException();
        }

        Response res = getMaterialAPIFeign.getTemporary(mediaId);

        return getResponseEntity(res);
    }


    @Override
    public MaterialOption.MaterialResponse addMaterial(String type, MultipartFile file, MaterialOption.VideoDescription videoDescription) {

        MaterialOption.MaterialType materialType = EnumUtils.getEnum(MaterialOption.MaterialType.class, type);
        if (materialType == null || file == null) {
            throw new BasicCustomException(BasicExceptionCode.INVALID_PARAM);
        }
        if (materialType.equals(MaterialOption.MaterialType.video) && (videoDescription == null || !videoDescription.valid())) {
            throw new BasicCustomException(BasicExceptionCode.INVALID_PARAM);
        }
        if (videoDescription == null) {
            videoDescription = new MaterialOption.VideoDescription();
        }

        JSONObject result = addMaterialRAW(file,type,videoDescription);

        if (!ResultCheckUtil.check(result)) {
            LOGGER.error(Objects.requireNonNull(result).toString());
            throw new BasicCustomException(ExceptionCode.UPLOAD_MATERIAL_FAIL);
        }
        return result.toJavaObject(MaterialOption.MaterialResponse.class);

    }

    @Override
    public ResponseEntity<byte[]> getMaterial(String mediaId){

        if (StringUtils.isBlank(mediaId)) {
            ExceptionUtils.throwInvalidParamException();
        }
        JSONObject mediaInfo = new JSONObject();
        mediaInfo.put("media_id",mediaId);
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> entity = new HttpEntity<>(mediaInfo.toJSONString(),headers);
        ResponseEntity<byte[]> response = restTemplate.exchange("https://api.weixin.qq.com/cgi-bin/material/get_material?access_token="+GlobalThreadLocalUtil.getAccessToken(),HttpMethod.POST,entity,byte[].class);
        if (response.getStatusCode().equals(HttpStatus.OK)) {
            HttpHeaders header =  response.getHeaders();
            if (header.getContentType()!=null) {
                if(MediaType.TEXT_PLAIN.equals(header.getContentType())){
                    header.setContentType(MediaType.APPLICATION_JSON);
                }
            }else{
                header.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            }
            return new ResponseEntity<>(response.getBody(),header,HttpStatus.OK);
        }
        return response;

    }

    private ResponseEntity<byte[]> getResponseEntity(Response res) {
        ResponseEntity<byte[]> response = null;
        if (res != null && res.status() == HttpStatus.OK.value()) {
            Map<String, Collection<String>> httpHeaders = res.headers();
            HttpHeaders headers = new HttpHeaders();
            for(String h:httpHeaders.keySet()){
               headers.addAll(h, (List<? extends String>) httpHeaders.get(h));
            }
            ResponseEntity.BodyBuilder builder = ResponseEntity.ok();
            InputStream inputStream = null;
            try {
                inputStream = res.body().asInputStream();
                byte[] b = new byte[Integer.parseInt(httpHeaders.get("content-length").iterator().next())];
                int count = 0;
                while((count=inputStream.read(b))!=-1){
                    LOGGER.debug("readed:{}",count);
                };

                if (httpHeaders.containsKey("content-type") && httpHeaders.get("content-type").contains(MediaType.IMAGE_JPEG_VALUE)) {
                    builder.contentType(MediaType.IMAGE_JPEG);
                    response = builder.body(b);
                } else if (httpHeaders.containsKey("content-type") && httpHeaders.get("content-type").contains(MediaType.TEXT_PLAIN_VALUE)) {
                    builder.contentType(MediaType.APPLICATION_JSON);
                    ResponseBack<?> responseb = ResponseBack.success(JSONObject.parseObject(new String(b)));
                    response = builder.body(JSON.toJSONBytes(responseb));
                }else{
                    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
                    response  = new ResponseEntity <>(b,headers, HttpStatus.OK);

                }
            } catch (IOException e) {
                LOGGER.error(e.getMessage(), e);
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        LOGGER.error(e.getMessage(),e);
                    }
                }
            }


        }
        return response;
    }

    /**
     * 上传媒体类永久素材特殊处理逻辑， 由于视频类需要POST2个表单
     */
    private JSONObject addMaterialRAW(MultipartFile file, String type, MaterialOption.VideoDescription videoDescription) {
        MaterialOption.MaterialType materialType = EnumUtils.getEnum(MaterialOption.MaterialType.class, type);

        try {

            HttpURLConnection huct = (HttpURLConnection) new URL("https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=" + GlobalThreadLocalUtil.getAccessToken()+"&type="+type).openConnection();
            huct.setRequestMethod("POST");//注意，只能以POST方式上传文件
            huct.setDoOutput(true);//设置可以有返回值

            huct.setRequestProperty("Content-Type", "multipart/form-data; boundary=-------------------146043902153");
            OutputStream os = huct.getOutputStream();
            //设置头部分割符
            String strb = "\r\n---------------------146043902153\r\n" +
                    "Content-Disposition:form-data;name=\"media\";" +
                    "filename=\"" + file.getOriginalFilename() + "\";" +//图片名称
                    "Content-Type:\"" + file.getContentType() + "\";encoding=utf-8;" +//video媒体文件类型和设置编码格式
                    "filelength:\"" + file.getSize() + "\"\r\n" +//文件大小
                    "application/octet-stream\r\n\r\n";
            os.write(strb.getBytes(StandardCharsets.UTF_8));

            os.write(file.getBytes());
            if(materialType.equals(MaterialOption.MaterialType.video)){
                String perpetual = "";//创建永久视频素材时，要额外添加表单数据
                perpetual += "\r\n---------------------146043902153\r\n";
                perpetual += "Content-Disposition:form-data;name=\"description\";\r\n\r\n";
                perpetual += (JSONObject.toJSONString(videoDescription));
                os.write(perpetual.getBytes(StandardCharsets.UTF_8));
            }
//尾部分割符
            os.write("\r\n---------------------146043902153--\r\n\r\n".getBytes());
            os.flush();
            os.close();

            InputStream is = huct.getInputStream();
            byte[] by = new byte[is.available()];
            int count = 0 ;
            while((count = is.read(by))!=-1){
                LOGGER.debug("read:{}",count);
            }
            return JSONObject.parseObject(new String(by));//返回数据

        } catch (IOException e) {
            LOGGER.error(e.getMessage(),e);
        }
        return new JSONObject();
    }

    @Override
    public MaterialOption.MaterialResponse addNews(List<Article> articles){

        if(CollectionUtils.isEmpty(articles)){
            ExceptionUtils.throwInvalidParamException();
        }if(articles.size()> MaterialOption.ARTICLES_LIMIT){
            throw new BasicCustomException(ExceptionCode.OUT_OF_ARTICLES_LIMIT);
        }
        JSONObject news = new JSONObject();
        news.put("articles",articles);

        JSONObject result = materialAPIFeign.addNews(news);
        if(!ResultCheckUtil.check(result)){
            throw new BasicCustomException(ExceptionCode.ADD_ARTICLES_FAIL);
        }
        return result.toJavaObject(MaterialOption.MaterialResponse.class);
    }


    @Override
    public void updateArticle(String mediaId, Integer index, Article article){
        if(StringUtils.isBlank(mediaId) || index==null ||article==null){
            ExceptionUtils.throwInvalidParamException();
        }

        JSONObject updateNews = new JSONObject();
        updateNews.put("media_id",mediaId);
        updateNews.put("index",index);
        updateNews.put("articles",article);
        JSONObject result = materialAPIFeign.updateNews(updateNews);
        if(!ResultCheckUtil.check(result)){
            throw new BasicCustomException(ExceptionCode.UPDATE_ARTICLE_FAIL);
        }
    }


    @Override
    public void deleteMaterial(String mediaId){
        if(StringUtils.isBlank(mediaId)){
            ExceptionUtils.throwInvalidParamException();
        }
        JSONObject mediaInfo = new JSONObject();
        mediaInfo.put("media_id",mediaId);
        JSONObject result = materialAPIFeign.deleteMaterial(mediaInfo);
        if(!ResultCheckUtil.check(result)){
            throw new BasicCustomException(ExceptionCode.UPDATE_ARTICLE_FAIL);
        }
    }

    @Override
    public MaterialOption.MaterialCountResponse getMaterialCount(){

        JSONObject result = materialAPIFeign.getMaterialCount();
        if(!ResultCheckUtil.check(result)){
            throw new BasicCustomException(ExceptionCode.GET_MATERIAL_COUNT_FAIL);
        }
        return result.toJavaObject(MaterialOption.MaterialCountResponse.class);
    }

    @Override
    public PagedResult<MaterialOption.MaterialItemResponse> batchGetMaterial(String type, int page, int limit){
        MaterialOption.MaterialType materialType = EnumUtils.getEnum(MaterialOption.MaterialType.class, type);
        if ( materialType == null) {
            ExceptionUtils.throwInvalidParamException();
        }
        JSONObject request = new JSONObject();
        int offset = (page-1)*limit;
        request.put("type",type);
        request.put("offset",offset);
        request.put("count",limit);
        JSONObject result = materialAPIFeign.batchGetMaterial(request);
        if(!ResultCheckUtil.check(result)){
            throw new BasicCustomException(ExceptionCode.GET_MATERIAL_LIST_FAIL);
        }

        int totalCount = result.getInteger("total_count");
        PagedResult<MaterialOption.MaterialItemResponse> pagedResult = new PagedResult<>(result.getJSONArray("item").toJavaList(MaterialOption.MaterialItemResponse.class),
                offset+limit,totalCount,page);
        pagedResult.setTotalPageNum((totalCount  +  limit  - 1) / limit);
        pagedResult.setPageSize(limit);
        return pagedResult;

    }
    @Override
    public PagedResult<MaterialOption.MaterialArticleItemResponse> batchGetArticleMaterial(int page, int limit){

        JSONObject request = new JSONObject();
        int offset = (page-1)*limit;
        request.put("type","news");
        request.put("offset",offset);
        request.put("count",limit);
        JSONObject result = materialAPIFeign.batchGetMaterial(request);
        if(!ResultCheckUtil.check(result)){
            throw new BasicCustomException(ExceptionCode.GET_MATERIAL_LIST_FAIL);
        }

        int totalCount = result.getInteger("total_count");
        JSONArray items = result.getJSONArray("item");
        List<MaterialOption.MaterialArticleItemResponse> list = new ArrayList<>();
        for(int i=0;i<items.size();i++){
            JSONObject item = items.getJSONObject(i);
            MaterialOption.MaterialArticleItemResponse itemResponse = new MaterialOption.MaterialArticleItemResponse();
            itemResponse.setMedia_id(item.getString("media_id"));
            itemResponse.setUpdate_time(item.getLong("update_time"));
            itemResponse.setArticles(item.getJSONObject("content").getJSONArray("news_item").toJavaList(Article.class));
            list.add(itemResponse);
        }
        PagedResult<MaterialOption.MaterialArticleItemResponse> pagedResult = new PagedResult<>(list,offset+limit,totalCount,page);
        pagedResult.setTotalPageNum((totalCount  +  limit  - 1) / limit);
        pagedResult.setPageSize(limit);
        return pagedResult;

    }

    @Override
    public MaterialOption.MaterialResponse uploadImgForArticle(MultipartFile file){

        if (file == null) {
            ExceptionUtils.throwInvalidParamException();
        }
        JSONObject result = materialAPIFeign.uploadImgForArticle(file);
        if (!ResultCheckUtil.check(result)) {
            LOGGER.error(result.toString());
            throw new BasicCustomException(ExceptionCode.UPLOAD_TEMPORARY_FAIL);
        }
        return result.toJavaObject(MaterialOption.MaterialResponse.class);
    }

}
